#include <jni.h>
#include <string>
#include <iostream>
#include <sstream>
#include <android/log.h>
#include "XArray.h"
#include "XMap.h"

#define TAG "JNI_CPP"
#define JLOG(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_myapplication_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    JLOG("stringFromJNI=%p", env);
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}
extern "C"
JNIEXPORT void JNICALL
Java_com_example_myapplication_MyActivity1_test1(JNIEnv *env, jobject thiz, jshort s, jint i,
                                                 jlong l, jfloat f, jdouble d, jchar c, jboolean z,
                                                 jbyte b, jstring str, jobject obj, jobject p,
                                                 jintArray arr) {
    JLOG("test1=%p", env);
    std::stringstream ss;
    ss << "short: " << s << std::endl;
    ss << "int: " << i << std::endl;
    ss << "long: " << l << std::endl;
    //显示小数
    ss.setf(std::ios::fixed);
    //显示2位小数
    ss.precision(2);
    ss << "float: " << f << std::endl;

    ss << "char: " << static_cast<uint8_t>(c) << std::endl;
    ss << "bool: " << static_cast<bool>(z) << std::endl;

    ss << "byte: " << static_cast<int>(b) << std::endl;
    const char *c_str = nullptr;
    jboolean isCopy = false;
    //https://blog.csdn.net/dreamInTheWorld/article/details/121372305
    c_str = env->GetStringUTFChars(str, &isCopy);
    ss << "string: " << c_str << std::endl;
    env->ReleaseStringUTFChars(str, c_str);

//    //按十六进制显示整数
//    ss.unsetf(ios_base::dec);
//    ss.setf(ios::hex);
//    ss << "number: " << number << endl;

    std::string text = ss.str();
    std::cout << text << std::endl;
    JLOG("%s", text.c_str());

}
extern "C"
JNIEXPORT jstring JNICALL
Java_com_example_myapplication_MyActivity1_testString_1String(JNIEnv *env, jobject thiz,
                                                              jstring str) {
    //https://blog.csdn.net/dreamInTheWorld/article/details/121372305
    char inputbuf[128];
    char outputbuf[128];
    int len = env->GetStringLength(str);
    env->GetStringUTFRegion(str, 0, len, inputbuf);
//    env->GetStringRegion(str,0,len,inputbuf);
    sprintf(outputbuf, "thanks %s", inputbuf);
    JLOG("[%s]inputbuf:%s", __FUNCTION__, inputbuf);
    JLOG("[%s]outputbuf:%s", __FUNCTION__, outputbuf);
    return env->NewStringUTF(outputbuf);
}
extern "C"
JNIEXPORT jintArray JNICALL
Java_com_example_myapplication_MyActivity1_testArray_1Array(JNIEnv *env, jobject thiz,
                                                            jintArray arr) {
    //==================================================================//
    //在c++中,java数组拷贝一份(使用c语言的malloc申请空间)
    //==================================================================//
    int arr_len = env->GetArrayLength(arr);
    jint *c_array = (jint *) malloc(sizeof(jint) * arr_len);
    memset(c_array, 0, sizeof(jint) * arr_len);
    env->GetIntArrayRegion(arr, 0, arr_len, c_array);
    for (jint i = 0; i < arr_len; i++) {
        c_array[i] += 100;
    }
    free(c_array);
    std::string str{"In c++: c_array = "};
    for (jint i = 0; i < arr_len; i++) {
        str += std::to_string(c_array[i]) + " ";
    }
    JLOG("%s", str.c_str());

    //==================================================================//
    //在c++中,java数组拷贝一份(使用jni接口申请空间的接口)
    //==================================================================//
    arr_len = env->GetArrayLength(arr);
    jboolean is_copy = JNI_FALSE;
    jint *j_array = env->GetIntArrayElements(arr, &is_copy);
    JLOG("arr_len = %d,is_copy=%d", arr_len, is_copy);
    std::stringstream ss;
    ss << "In c++: j_array = ";
    for (int i = 0; i < arr_len; i++) {
        ss << j_array[i] << " ";
    }
    JLOG("%s", ss.str().c_str());
    for (int i = 0; i < arr_len; i++) {
        j_array[i] += 10;
    }
    //==================================================================//
    //0 清理j_arr 并复制j_arr的内容到java数组
    //JNI_COMMIT 复制j_arr中的内容到java数组不清理j_arr
    //JNI_ABORT 清理j_arr 不修改arr数组
    //==================================================================//
    env->ReleaseIntArrayElements(arr, j_array, 0);

    //==================================================================//
    //在jni中new 一个java的数组，返回给java，java负责回收
    //==================================================================//
    jintArray retArr = env->NewIntArray(10);
    jint buf[5] = {0};
    env->GetIntArrayRegion(arr, 5, 5, buf);//给buf填充数据
    buf[0] = 100;
    buf[1] = 101;
    buf[4] = 105;
    env->SetIntArrayRegion(retArr, 5, 5, buf);

    //==================================================================//
    //直接获取java数组地址，并暂停GC
    //直接修改传入数组的值，方便处理大数据，减少内存拷贝
    //==================================================================//
    jint *ref_arr = static_cast<jint *>(env->GetPrimitiveArrayCritical(arr, nullptr));
    ref_arr[0] = 999;
    ref_arr[9] = 111;
    //恢复GC
    env->ReleasePrimitiveArrayCritical(arr, ref_arr, 0);

    return retArr;//arr;
}

//获取java中的静态成员变量，并修改
extern "C"
JNIEXPORT jstring JNICALL
Java_com_example_myapplication_MyActivity1_TestCppCallJava(JNIEnv *env, jobject thiz) {
    JLOG("in c++ : TestCppCallJava");

    //==================================================================//
    //c++调用java的静态成员变量
    //==================================================================//
    //获取java类
    jclass clazz = env->GetObjectClass(thiz);

    //获取java类中静态成员变量的属性ID
    jfieldID fID = env->GetStaticFieldID(clazz, "m_int", "I");

    //GetStatic<成员变量类型>Field
    jint m_int = env->GetStaticIntField(clazz, fID);
    JLOG("in c++ [before]: m_int = %d", m_int);
    env->SetStaticIntField(clazz, fID, 100);//执行完这行之后，静态成员变量的值就已经修改过来了
    m_int = env->GetStaticIntField(clazz, fID);
    JLOG("in c++ [after]: m_int = %d", m_int);

    //==================================================================//
    //c++调用java的静态成员方法
    //==================================================================//
    //获取java类中静态成员变量的属性ID

    jmethodID mID = env->GetStaticMethodID(clazz, "JavaForCpp",
                                           "(Ljava/lang/String;I)Ljava/lang/String;");

    jstring str = env->NewStringUTF("cpp data string 001");

    //GetStatic<返回值类型>Method
    jstring ret = static_cast<jstring>(env->CallStaticObjectMethod(clazz, mID, str, 999));
    const char *retstr = env->GetStringUTFChars(ret, nullptr);
    JLOG("in c++ return = %s", retstr);
    env->ReleaseStringUTFChars(ret, retstr);

    //==================================================================//
    //c++调用java的非静态成员方法
    //比如，使用java中的String,在c++中创建好内容之后，再以返回值的形式返回java
    //==================================================================//

    // 1、获取String类的class引用
    jclass cla_String = env->FindClass("java/lang/String");

    // 2、获取String的构造方法ID(构造方法的名统一为：<init>)
    jmethodID init_String = env->GetMethodID(cla_String, "<init>", "(Ljava/lang/String;)V");
    // 4、创建String对象的实例(调用对象的构造方法并初始化对象)
    jobject obj_String = env->NewObject(cla_String, init_String, env->NewStringUTF("cat_123_Test"));
    // 5、调用String类的length方法和toUpperCase方法
    jmethodID length_String = env->GetMethodID(cla_String, "length", "()I");
    jint len = env->CallIntMethod(obj_String, length_String);
    jmethodID toUpper_String = env->GetMethodID(cla_String, "toUpperCase", "()Ljava/lang/String;");
    jstring upper_ = (jstring) env->CallObjectMethod(obj_String, toUpper_String);
    const char *upper = env->GetStringUTFChars(upper_, nullptr);
    JLOG("in c++: len = %d,upper=%s", len, upper);
    env->ReleaseStringUTFChars(upper_, upper);
    return static_cast<jstring>(obj_String);
}
extern "C"
JNIEXPORT jobject JNICALL
Java_com_example_myapplication_MyActivity1_TestIData(JNIEnv *env, jobject thiz, jobject data) {
    //==================================================================//
    //c++中打印在java中的自定义数据类型
    //==================================================================//
    jclass IData = env->FindClass("com/example/myapplication/IData");
    jmethodID IData_init = env->GetMethodID(IData, "<init>", "()V");
    jfieldID IData_m_txt = env->GetFieldID(IData, "m_txt", "Ljava/lang/String;");
    //主要是为了这个属性...尝试修改一下这个属性
    jobject IData_m_txt_str = env->GetObjectField(data, IData_m_txt);
    const char *m_txt = env->GetStringUTFChars((jstring) IData_m_txt_str, nullptr);
    JLOG("In c++ IData 111 m_txt=%s", m_txt);
    env->ReleaseStringUTFChars((jstring) IData_m_txt_str, m_txt);
    JLOG("In c++ IData 222 m_txt=%s", m_txt);
    jstring newJavaSting = env->NewStringUTF("直接修改试试");
    env->SetObjectField(data, IData_m_txt, newJavaSting);


    //==================================================================//
    //c++调用在java中的自定义数据类型
    //注意和上面的String对比
    //==================================================================//
    // 1、获取IData类的class引用
    jclass cla_IData = env->FindClass("com/example/myapplication/IData");

    // 2、获取IData的构造方法ID(构造方法的名统一为：<init>)
    jmethodID init_IData = env->GetMethodID(cla_IData, "<init>", "()V");

    // 3、创建IData对象，不调用构造
    jobject obj_IData = env->AllocObject(cla_IData);
    // 4、调用构造函数,给obj_IData分配内存
    env->CallVoidMethod(obj_IData, init_IData);

    //5、给内部属性m_txt赋值
    jfieldID fID = env->GetFieldID(cla_IData, "m_txt", "Ljava/lang/String;");
    env->SetObjectField(obj_IData, fID, env->NewStringUTF("from c++ Data txt"));
    return obj_IData;//data;
}
extern "C"
JNIEXPORT jobject JNICALL
Java_com_example_myapplication_MyActivity1_TestArrayList(JNIEnv *env, jobject thiz, jobject as) {
    // TODO: implement TestArrayList()
    XArray xarr;
    xarr.Init(env);
    auto cas = xarr.ToVS(as);
    for (auto &s: cas) {
        JLOG("In C++ para:%s", s.c_str());
    }
    std::vector<std::string> res;
    res.push_back("C++ Data 001");
    res.push_back("C++ Data 002");
    res.push_back("C++ Data 003");
    res.push_back("C++ Data 004");

    return xarr.ToArrayList(res);
}

jobject TestRegCpp(JNIEnv *env, jobject thiz, jobject hss) {
    // TODO: implement TestRegCpp()

    JLOG("In C++ call TestRegCpp ");
    XMap xmap;
    xmap.init(env);
    std::map<std::string,std::string> cmss = xmap.convertJavaHashMapToCppMap(hss);

    for(auto itr = cmss.begin();itr!=cmss.end();itr++){
        JLOG("In C++:%s:%s",itr->first.c_str(),itr->second.c_str());
    }
    std::map<std::string,std::string> re;
    re["c++key01"] = "c++value01";
    re["c++key02"] = "c++value02";
    re["c++key03"] = "c++value03";

    return xmap.convertCppMapToJavaHashMap(re);
}

/*
 * 测试动态注册jni绑定的函数
 */
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    JLOG("In C++ ： begin JNI_OnLoad ...");
    JNIEnv *env = nullptr;
    vm->GetEnv((void **) &env, JNI_VERSION_1_6);
    //找到类package com.example.myapplication.MyActivity1
    jclass cla = env->FindClass("com/example/myapplication/MyActivity1");
    //jobject TestRegCpp(JNIEnv *env, jobject thiz, jobject hss)
    //private native HashMap<String,String> TestRegCpp(HashMap<String,String> hss);
    static const JNINativeMethod methods[] = {
            {"TestRegCpp", "(Ljava/util/HashMap;)Ljava/util/HashMap;", (void *) TestRegCpp}
    };
    int re = env->RegisterNatives(cla, methods, sizeof(methods) / sizeof(JNINativeMethod));
    if(re != JNI_OK){
        JLOG("RegisterNatives failed");
    }else{
        JLOG("RegisterNatives succeed");
    }

    jmethodID jonSendCb = env->GetMethodID(cla,  "onSend", "([B)I");
//    const jbyte* your_byte_array;
    const jbyte your_byte_array[] = {1, 2, 3, 4, 5};
    jsize length = sizeof(your_byte_array)/sizeof(jbyte);

    std::stringstream ss;
    ss << "In c++: j_array = ";
    for (int i = 0; i < length; i++) {
        ss << static_cast<int>(your_byte_array[i]) << " ";
    }
    JLOG("%s", ss.str().c_str());

    jbyteArray msgArray = env->NewByteArray(length);
    env->SetByteArrayRegion(msgArray, 0, length, your_byte_array);

    env->CallIntMethod(env->NewObject(cla,env->GetMethodID(cla,"<init>","()V")), jonSendCb, msgArray);
    env->DeleteLocalRef(msgArray);
    env->DeleteLocalRef(cla);

    return JNI_VERSION_1_6;
}